package com.jdk.demo.nio.半包粘包.new1;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class NioSocketServer extends Thread {
	ServerSocketChannel serverSocketChannel = null;
	Selector selector = null;
	SelectionKey selectionKey = null;
	// 缓存一个read事件中一个不完整的包，以待下次read事件到来时拼接成完整的包
	ByteBuffer cacheBuffer = ByteBuffer.allocate(1024);
	ByteBuffer cacheHeader = ByteBuffer.allocate(4);
	boolean cache = false;
	boolean cacheH = false;
	int bodyLen = -1;
	int left = -1;

	public void initServer() throws IOException {
		selector = Selector.open();
		serverSocketChannel = ServerSocketChannel.open();
		serverSocketChannel.configureBlocking(false);
		serverSocketChannel.socket().bind(new InetSocketAddress(8888));
		selectionKey = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
	}

	public void run() {
		while (true) {
			try {
				int selectKey = selector.select();
				if (selectKey > 0) {
					Set<SelectionKey> keySet = selector.selectedKeys();
					Iterator<SelectionKey> iter = keySet.iterator();
					while (iter.hasNext()) {
						SelectionKey selectionKey = iter.next();
						iter.remove();
						if (selectionKey.isAcceptable()) {
							accept(selectionKey);
						}
						if (selectionKey.isReadable()) {
							read(selectionKey);
						}
					}
				}
			} catch (IOException e) {
				e.printStackTrace();
				try {
					serverSocketChannel.close();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			}
		}
	}

	public void accept(SelectionKey key) {
		try {
			ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
			SocketChannel socketChannel = serverSocketChannel.accept();
			System.out.println("is acceptable");
			socketChannel.configureBlocking(false);
			socketChannel.register(selector, SelectionKey.OP_READ);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public void read(SelectionKey selectionKey) {
		int head_length = 4;//数据包长度
		byte[] headByte = new byte[head_length];

		try {
			SocketChannel channel = (SocketChannel) selectionKey.channel();
			ByteBuffer byteBuffer = ByteBuffer.allocate(10);
			channel.read(byteBuffer);// 当前read事件
			byteBuffer.flip();// write mode to read mode
			while (byteBuffer.remaining() > 0) {
				if (bodyLen == -1) {// 还没有读出包头，先读出包头
					if(cacheH){
						int limit = byteBuffer.limit();
						byteBuffer.limit(cacheHeader.limit() - cacheHeader.position());
						cacheHeader.put(byteBuffer);
						cacheHeader.flip();
						cacheHeader.get(headByte);
						bodyLen = byteArrayToInt(headByte);
						left = bodyLen;
						byteBuffer.limit(limit);
						cacheHeader.clear();
						cacheH = false;
					}else{
						if (byteBuffer.remaining() >= head_length) {// 可以读出包头，否则缓存
							byteBuffer.get(headByte);
							bodyLen = byteArrayToInt(headByte);
							left = bodyLen;
						} else {
							cacheHeader.put(byteBuffer);
							cacheH = true;
						}
					}
				} else {// 已经读出包头
					if (byteBuffer.remaining() >= left) {// 大于等于一个包，直接读取；否则缓存
						byte[] bodyByte = new byte[bodyLen];
						cacheBuffer.flip();
						cacheBuffer.get(bodyByte, 0, cacheBuffer.limit());
						byteBuffer.get(bodyByte, cacheBuffer.position(), left);

						bodyLen = -1;
						left = -1;
						cacheBuffer.clear();
						cache = false;

						System.out.println("receive from clien content is:" + new String(bodyByte));
						continue;
					} else {
						left -= byteBuffer.limit() - byteBuffer.position();
						cacheBuffer.put(byteBuffer);
						cache = true;
					}
				}
			}
		} catch (IOException e) {
			try {
				serverSocketChannel.close();
				selectionKey.cancel();
			} catch (IOException e1) {
				e1.printStackTrace();
			}
			e.printStackTrace();
		}
	}

	/**
	 * byte[]转int
	 *
	 * @param bytes
	 * @return
	 */
	private static int byteArrayToInt(byte[] bytes) {
		int value = 0;
		// 由高位到低位
		for (int i = 0; i < 4; i++) {
			int shift = (4 - 1 - i) * 8;
			value += (bytes[i] & 0x000000FF) << shift;// 往高位游
		}
		return value;
	}

	public static void main(String args[]) throws IOException {
		NioSocketServer server = new NioSocketServer();
		server.initServer();
		server.start();
	}

}
